// Package controllers 包含应用程序的控制器逻辑。
// 本文件用于处理文章相关的 HTTP 请求。
package controllers

import (
	"encoding/json"      // 提供 JSON 编码和解码功能
	"errors"             // 提供错误处理功能
	"exchangeapp/global" // 导入全局变量包，用于访问数据库和 Redis 客户端
	"exchangeapp/models" // 导入模型包，用于操作数据库表
	"fmt"
	"net/http" // 提供 HTTP 请求和响应的相关功能
	"time"     // 用于设置缓存过期时间

	"github.com/gin-gonic/gin"  // Gin 是一个轻量级的 Web 框架
	"github.com/go-redis/redis" // Redis 客户端库
	"gorm.io/gorm"              // GORM 是一个流行的 ORM 框架
)

// cacheKey 是 Redis 中存储文章列表的键(换句话说,实际上,我们再redis中存储的就是articles:值)
var cacheKey = "articles"

// CreateArticle 创建文章
// @Summary 创建新文章
// @Description 接收 JSON 格式的文章数据并存入数据库
// @Tags 文章管理
// @Accept json
// @Produce json
// @Param article body models.Article true "文章数据"
// @Success 201 {object} models.Article "成功创建的文章"
// @Failure 400 {object} gin.H "请求数据格式错误"
// @Failure 500 {object} gin.H "服务器错误"
// @Router /articles [post]
func CreateArticle(ctx *gin.Context) {
	// 声明一个 Article 结构体变量，用于接收请求数据
	var article models.Article

	// 将请求体中的 JSON 数据绑定到 article 变量
	if err := ctx.ShouldBindJSON(&article); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	fmt.Printf("JSON 绑定成功 - Title: %s (长度: %d)\n", article.Title, len(article.Title))
	fmt.Printf("JSON 绑定成功 - Content: %s (长度: %d)\n", article.Content, len(article.Content))
	fmt.Printf("JSON 绑定成功 - Preview: %s (长度: %d)\n", article.Preview, len(article.Preview))

	// 4. 检查字符编码
	fmt.Printf("字符检查 - Title 字节: %v\n", []byte(article.Title))
	fmt.Printf("字符检查 - Content 字节: %v\n", []byte(article.Content))

	// 自动迁移文章表结构
	if err := global.Db.AutoMigrate(&article); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 插入新文章记录到数据库
	if err := global.Db.Create(&article).Error; err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 删除 Redis 缓存中的文章列表(缓存失效,保证一定会有缓存击穿)
	if err := global.RedisDB.Del(cacheKey).Err(); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 返回成功创建的文章
	ctx.JSON(http.StatusCreated, article)
}

// GetArticles 获取所有文章
// @Summary 获取所有文章
// @Description 从数据库或缓存中查询所有文章
// @Tags 文章管理
// @Produce json
// @Success 200 {array} models.Article "文章列表"
// @Failure 500 {object} gin.H "服务器错误"
// @Router /articles [get]
func GetArticles(ctx *gin.Context) {
	// 从 Redis 缓存中获取文章列表
	cachedData, err := global.RedisDB.Get(cacheKey).Result()

	if err == redis.Nil {
		// 如果缓存不存在，从数据库中查询文章
		var articles []models.Article

		if err := global.Db.Find(&articles).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
			} else {
				ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			}
			return
		}

		// 将查询结果序列化为 JSON 并存入 Redis 缓存
		articleJSON, err := json.Marshal(articles)
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		if err := global.RedisDB.Set(cacheKey, articleJSON, 10*time.Minute).Err(); err != nil {
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		// 返回查询结果
		ctx.JSON(http.StatusOK, articles)

	} else if err != nil {
		// 如果发生其他错误，返回 500 错误
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	} else {
		// 如果缓存存在，解析缓存数据并返回(这里有些麻烦了,直接返回cachedData给客户端就可以了)
		var articles []models.Article

		if err := json.Unmarshal([]byte(cachedData), &articles); err != nil {
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		ctx.JSON(http.StatusOK, articles)
	}
}

// GetArticleByID 根据 ID 获取文章
// @Summary 获取文章详情
// @Description 根据文章 ID 查询文章详情
// @Tags 文章管理
// @Param id path string true "文章 ID"
// @Success 200 {object} models.Article "文章详情"
// @Failure 404 {object} gin.H "文章未找到"
// @Failure 500 {object} gin.H "服务器错误"
// @Router /articles/{id} [get]
func GetArticleByID(ctx *gin.Context) {
	// 获取文章 ID 参数(通过/article/:id得到)
	id := ctx.Param("id")

	// 声明一个 Article 结构体变量，用于存储查询结果
	var article models.Article

	// 根据 ID 查询文章
	if err := global.Db.Where("id = ?", id).First(&article).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		} else {
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		}
		return
	}

	// 返回查询结果
	ctx.JSON(http.StatusOK, article)
}
